/***************************************************************************
*                                                                         *
*   This file is part of the Fotowall project,                            *
*       http://code.google.com/p/fotowall                                 *
*                                                                         *
*   Copyright (C)                                                         *
*       2007-2009 by Enrico Ros <enrico.ros@gmail.com>                    *
*       2007 Riccardo Iaconelli <ruphy@kde.org>                           *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
 * ----------------------------------------------------------------------- *
 *																		   *
 *	Modifications for FotoKinect, Spring 2011 by Gianna Badiali,		   *
 *                                                   Gabriel Fernandez,	   *
 *                                                   Kelsey Tripp, and     *
 *                                                   Sam Swarr             *
 *																		   *
 ***************************************************************************/

#include "ColorPickerItem.h"
#include <QtCore/QtGlobal>
#include <QtCore/QTimer>
#include <QtGui/QPainter>
#include <QtGui/QStyleOptionGraphicsItem>
#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtCore/QTimeLine>
#include <iostream>

#define COLORPICKER_MARGIN 8
#define COLORPICKER_SPACING 10
#define COLORPICKER_VALUE_WIDTH 16
#define COLORPICKER_SQUARE_W 40
#define COLORPICKER_SQUARE_D 20
#define _TRUE false

ColorPickerItem::ColorPickerItem(int width, int height, QGraphicsItem * parent)
	: QGraphicsItem(parent)
	, m_hue(0.0)
	, m_sat(1.0)
	, m_val(1.0)
	, m_size(width, height)
	, m_isAnimated(_TRUE)
	, m_timeLine(0)
	, m_scale(0.0)
	, m_anchor(AnchorCenter)
{
	int left = COLORPICKER_MARGIN;
	int top = COLORPICKER_MARGIN;
	int right = width - COLORPICKER_MARGIN;
	int bottom = height - COLORPICKER_MARGIN;
	m_hueSatRect = QRect(left, top, right - COLORPICKER_VALUE_WIDTH - COLORPICKER_MARGIN - left, bottom - top);
	m_valRect = QRect(right - COLORPICKER_VALUE_WIDTH, top, COLORPICKER_VALUE_WIDTH, bottom - top);

	setAcceptsHoverEvents(true);

}

void ColorPickerItem::setColor(const QColor & color)
{
	m_hue = color.hueF();
	m_sat = color.saturationF();
	m_val = color.valueF();
	if (m_hue < 0.0)
		m_hue = 0.0;
	regenHueSatPixmap();
	regenValPixmap();
	update();
}

QColor ColorPickerItem::color() const
{
	return QColor::fromHsvF(m_hue, m_sat, m_val);
}

void ColorPickerItem::setAnimated(bool animated)
{
	if (m_isAnimated == animated)
		return;

	// start animations
	m_isAnimated = animated;
	if (animated) {
		if (!m_timeLine) {
			m_timeLine = new QTimeLine(300, this);
			m_timeLine->setStartFrame(0);
			m_timeLine->setEndFrame(100);
			m_timeLine->setUpdateInterval(20);
			connect(m_timeLine, SIGNAL(frameChanged(int)), this, SLOT(slotAnimateScale(int)));
		}
	}

	// destroy animations
	else {
		delete m_timeLine;
		m_timeLine = 0;
		m_scale = 1.0;
		setTransform(QTransform());
	}
}

bool ColorPickerItem::animated() const
{
	return m_isAnimated;
}

void ColorPickerItem::setAnchor(Anchor anchor)
{
	if (m_anchor != anchor) {
		m_anchor = anchor;
	}
}

ColorPickerItem::Anchor ColorPickerItem::anchor() const
{
	return m_anchor;
}

QRectF ColorPickerItem::boundingRect() const
{
	return QRect(0, 0, m_size.width(), m_size.height());
}

void ColorPickerItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{
	if (m_isAnimated) {
		painter->setRenderHints(QPainter::Antialiasing, true);
		if (m_scale > 0.9)
			painter->setRenderHints(QPainter::SmoothPixmapTransform, true);
	}

	// draw hue-sat pixmap
	if (m_hueSatPixmap.size() != m_hueSatRect.size()) {
		regenHueSatPixmap(); //also necessary.
	}
	painter->drawPixmap(m_hueSatRect.topLeft(), m_hueSatPixmap); //this is important to make it not transparent.
	painter->setOpacity(1.0);
	painter->setPen(QPen(Qt::black, 1));
	painter->setBrush(Qt::NoBrush);
	painter->drawRect(m_hueSatRect.adjusted(0, 0, -1, 0)); //we want the big square. 

	// draw hue-sat cross
	int crossX = m_hueSatRect.left() + (int)(m_hue * (qreal)(m_hueSatRect.width() - 1));
	int crossY = m_hueSatRect.top() + (int)((1.0 - m_sat) * (qreal)(m_hueSatRect.height() - 1));
	qreal crossVal = m_val + 0.5;
	if (crossVal > 1.0)
		crossVal -= 1.0;
	painter->setPen(QPen(QColor::fromHsvF(0, 0, crossVal), 1));
	painter->drawLine(crossX, crossY - 2, crossX, crossY - 6);
	painter->drawLine(crossX, crossY + 2, crossX, crossY + 6);
	painter->drawLine(crossX - 6, crossY, crossX - 2, crossY);
	painter->drawLine(crossX + 6, crossY, crossX + 2, crossY);

	painter->setBrush(Qt::NoBrush);

	// draw value pixmap (this is the little bar on the side)
	if (m_valPixmap.size() != m_valRect.size())
		regenValPixmap();
	painter->drawPixmap(m_valRect.topLeft(), m_valPixmap);
	painter->setPen(QPen(Qt::black, 1));
	if (m_isAnimated)
		painter->drawRect(m_valRect.adjusted(0, 0, -1, 0));
	else
		painter->drawRect(m_valRect.adjusted(0, -1, -1, 0));

	// draw value indicator
	int valueX = m_valRect.left();
	int valueY = m_valRect.top() + (int)((1.0 - m_val) * (qreal)(m_valRect.height() - 1));
	painter->setPen(Qt::NoPen);
	QPainterPath indicatorPath;
	indicatorPath.moveTo(valueX + 5.0, valueY);
	indicatorPath.lineTo(valueX - 4.0, valueY + 3.5);
	indicatorPath.lineTo(valueX - 4.0, valueY - 3.5);
	painter->setBrush(Qt::black);
	painter->setRenderHints(QPainter::Antialiasing, true);
	painter->drawPath(indicatorPath);
	painter->setRenderHints(QPainter::Antialiasing, false);
}

void ColorPickerItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
	if (event->button() == Qt::LeftButton)
		pickColor(event->pos().toPoint());
}

void ColorPickerItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
	if (event->buttons() & Qt::LeftButton)
		pickColor(event->pos().toPoint());
}


void ColorPickerItem::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
{
	if (m_timeLine) {
		m_timeLine->setDirection(QTimeLine::Forward);
		if (m_timeLine->state() != QTimeLine::Running)
			m_timeLine->start();
	}
}

void ColorPickerItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
{
	if (m_timeLine) {
		m_timeLine->setDirection(QTimeLine::Backward);
		if (m_timeLine->state() != QTimeLine::Running)
			m_timeLine->start();
	}
}


// chooses the hue or value of the colorpicker according to the given point
void ColorPickerItem::pickColor(const QPoint & pos)
{
	// check for change on the hue-sat wheel
	if (m_hueSatRect.contains(pos)) {
		qreal newHue = (pos.x() - m_hueSatRect.x()) / (qreal)(m_hueSatRect.width() /*-1*/);
		qreal newSat = 1.0 - (pos.y() - m_hueSatRect.top()) / (qreal)(m_hueSatRect.height() - 1);
		if (newHue != m_hue || newSat != m_sat) {
			m_hue = newHue;
			m_sat = newSat;
			regenValPixmap();
			update();
			emit colorChanged(color());
		}
	}

	// check for change on the value slider
	else if (m_valRect.contains(pos)) {
		qreal newVal = 1.0 - (pos.y() - m_valRect.top()) / (qreal)(m_valRect.height() - 1);
		if (newVal != m_val) {
			m_val = newVal;
			regenHueSatPixmap();
			update();
			emit colorChanged(color());
		}
	}
}


// chooses the hue of the colorpicker according to the given point
void ColorPickerItem::pickHue(const QPoint & pos)
{
	// check for change on the hue-sat wheel
	if (m_hueSatRect.contains(pos)) {
		qreal newHue = (pos.x() - m_hueSatRect.x()) / (qreal)(m_hueSatRect.width() /*-1*/);
		qreal newSat = 1.0 - (pos.y() - m_hueSatRect.top()) / (qreal)(m_hueSatRect.height() - 1);
		if (newHue != m_hue || newSat != m_sat) {
			m_hue = newHue;
			m_sat = newSat;
			regenValPixmap();
			update();
			emit colorChanged(color());
		}
	}
}


// chooses the value of the colorpicker according to the given point
void ColorPickerItem::pickValue(const QPoint & pos) {
	// check for change on the value slider
	if (m_valRect.contains(pos)) {
		qreal newVal = 1.0 - (pos.y() - m_valRect.top()) / (qreal)(m_valRect.height() - 1);
		if (newVal != m_val) {
			m_val = newVal;
			regenHueSatPixmap();
			update();
			emit colorChanged(color());
		}
	}
}


// resets the color of the colorpicker to the given color
void ColorPickerItem::resetColor(QColor color) {
	this->setColor(color);
}


// creates the hue-saturation pixmap
void ColorPickerItem::regenHueSatPixmap()
{
	// generate the h-s spectrum
	int width = m_hueSatRect.width();
	int height = m_hueSatRect.height();

	QImage hueSatSpectrum(width, height, QImage::Format_ARGB32);
	for (int y = 0; y < height; y++) {
		qreal sat = (qreal)(height - y) / (qreal)height;
		QRgb * imagePtr = (QRgb*)hueSatSpectrum.scanLine(y);
		for (int x = 0; x < width; x++)
			*imagePtr++ = QColor::fromHsvF((qreal)x / (qreal)width, sat, m_val).rgb();
	}
	m_hueSatPixmap = QPixmap::fromImage(hueSatSpectrum);
}


// creates the value pixmap
void ColorPickerItem::regenValPixmap() //this works and is necessary.
{
	// generate the value gradient
	int width = m_valRect.width();
	int height = m_valRect.height();

	QImage valSpectrum(width, height, QImage::Format_ARGB32);
	for (int y = 0; y < height; y++) {
		QRgb * imagePtr = (QRgb*)valSpectrum.scanLine(y);
		quint32 color = QColor::fromHsvF(m_hue, m_sat, (qreal)(height - y) / (qreal)height).rgb();
		for (int x = 0; x < width; x++)
			*imagePtr++ = color;
	}
	m_valPixmap = QPixmap::fromImage(valSpectrum);
}
